package com.heo.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import org.apache.log4j.Logger;
import com.heo.coder.packet.AttributePacketRsp;
import com.heo.coder.packet.CommPacketRsp;
import com.heo.coder.packet.ControlPacketRsp;
import com.heo.coder.packet.ParamsPacketRsp;
import com.heo.coder.packet.QueryLocationPacketRsp;
import com.heo.coder.packet.RegisterPacketRsp;
import com.heo.coder.packet.SettingPacketRsp;
import com.heo.coder.packet.SocketPacket;
import com.heo.coder.packet.base.BasePacket;
import com.heo.thread.SendMessageSocketThread;
import com.heo.util.ByteHelper;
import com.heo.util.DeviceCommand;
import com.heo.util.NettyChannelMapUtils;
import com.heo.util.Utils;
import com.heo.pojo.AttributeInfoEntity;
import com.heo.pojo.DeviceInfoEntity;
import com.heo.pojo.DeviceModelEntity;
import com.heo.pojo.LocationInfoEntity;
import com.heo.pojo.OrgInfoEntity;
import com.heo.pojo.RegisterEntity;
import com.heo.pub.ApplicationContextUtil;
import com.heo.pub.DictEnum;
import com.heo.service.DeviceInfoService;
import com.heo.service.DeviceModelService;
import com.heo.service.OrgInfoService;

import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class NettyServerSocketHandler extends ChannelInboundHandlerAdapter {
	private Logger log = Logger.getLogger(this.getClass());
	private short sendSequenceId = 1;
	private String logHeader = "NettyServerSocketHandler";
	private String deviceImei = null;
	public ChannelHandlerContext mCtx;
	private BasePacket basePacket = null;
	private String appId = "";
	private String callUrl = "";
	// 初始化设备，并入库
    DeviceInfoService deviceInfoService = null;
    DeviceModelService deviceModelService = null;
    DeviceInfoEntity deviceInfo = null;

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		log.error(logHeader + "[断开]操作异常，socket连接已断开。");
		log.error(cause.getMessage(), cause);
		// 网络异常，断开。
		ctx.close();
	}

	/**
	 * 客户端连接上来的时候触发 连接上来的时候，并不知道客户端是谁，需要根据他后面发的指令才知道是谁。
	 */
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		this.mCtx = ctx;
		String channelId = ctx.channel().id().asShortText();
		String remoteIpAddress = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
		logHeader = "[server][" + channelId + "][" + remoteIpAddress + "]";
		log.info(logHeader + "[连接]与客户端建立连接成功。");
	}

	/**
	 * 连接关闭成功后触发
	 */
	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		String channelId = ctx.channel().id().asShortText();
		log.info(logHeader + "[断开]socket连接已断开。" + channelId);
	}

	/**
	 * 收到客户端传上来的数据触发
	 */
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		basePacket = (SocketPacket) msg;
		log.info(basePacket);
		switch (basePacket.getCommandId()) {
		case DeviceCommand.AUTHE_COMMAND_REQ:
			log.info("鉴权开始   鉴权码 = " + basePacket.getCommandId() + "---" + basePacket.toString());
			// 初始化设备，并入库
		    deviceInfoService = (DeviceInfoService) ApplicationContextUtil.getBeanById("deviceInfoService");
		    deviceModelService = (DeviceModelService) ApplicationContextUtil.getBeanById("deviceModelService");
		    deviceImei = "000" + basePacket.getImei();
		    deviceInfo = deviceInfoService.getDeviceInfoMapByImei(deviceImei);
		    if(deviceInfo == null) {
		    	throw new Exception(deviceImei + "设备不存在请联系管理员!!");
		    }
		    OrgInfoService orgInfoService  = (OrgInfoService) ApplicationContextUtil.getBeanById("orgInfoService");
		    OrgInfoEntity orgInfoEntity = orgInfoService.getByKey(deviceImei);
		    appId = orgInfoEntity.getAppId();
		    callUrl = orgInfoEntity.getCallUrl();
		    DeviceModelEntity deviceModel = deviceModelService.getDeviceModelMap(deviceInfo.getDeviceModelId());
		    NettyChannelMapUtils.add(appId, deviceImei, deviceInfo.getDeviceChannelNum(), Utils.getShort(deviceModel.getDeviceModel()), this);
			CommPacketRsp commRsp = new CommPacketRsp(DeviceCommand.COMM_COMMAND_RSP, basePacket.getReserve(), basePacket.getSubcontract(), basePacket.getRsa(), basePacket.getBodyLength(), 
					basePacket.getImei(), basePacket.getSequenceId(), basePacket.getBody(), basePacket.getCheck());
			byte[] bytes = new byte[] { 0 };
			commRsp.setMsgContent(basePacket.getSequenceId(), basePacket.getCommandId(), bytes);
			sendPacket(ctx.channel(), commRsp);
			
			SettingPacketRsp settingPacketRsp = new SettingPacketRsp(DeviceCommand.SETTING_PARAMS_COMMAND_RSP, basePacket.getReserve(), basePacket.getSubcontract(), basePacket.getRsa(), 0, 
					basePacket.getImei(), getSendSequenceIdHex(), "", "");
			settingPacketRsp.setMsgContent("", "click1.cn", 7000, 120, 60);
			SendMessageSocketThread sendMessageSocketThread = new SendMessageSocketThread(appId,
					deviceImei, settingPacketRsp, 10000);
			NettyChannelMapUtils.sendTaskThreadPool.execute(sendMessageSocketThread);
			
//			ControlPacketRsp controlPacketRsp = new ControlPacketRsp(DeviceCommand.CONTROL_COMMAND_RSP, basePacket.getReserve(), basePacket.getSubcontract(), basePacket.getRsa(), 0, 
//					basePacket.getImei(), getSendSequenceIdHex(), "", "");
//			controlPacketRsp.openBike("01000000");
//			SendMessageSocketThread openMessageSocketThread = new SendMessageSocketThread(appId,
//					deviceImei, controlPacketRsp, 20000);
//			NettyChannelMapUtils.sendTaskThreadPool.execute(openMessageSocketThread);
			break;
		case DeviceCommand.HEARTBEAT_COMMAND_REQ:
			log.info("终端心跳  心跳码= " + basePacket.getCommandId() + "---" + basePacket.toString());
			NettyChannelMapUtils.refreshDeviceControlInfoExpire(appId, deviceImei);// 设备有消息上来，在redis里做延期
			CommPacketRsp heartCommRsp = new CommPacketRsp(DeviceCommand.COMM_COMMAND_RSP, basePacket.getReserve(), basePacket.getSubcontract(), basePacket.getRsa(), basePacket.getBodyLength(), 
					basePacket.getImei(), basePacket.getSequenceId(), basePacket.getBody(), basePacket.getCheck());
			byte[] heartBytes = new byte[] { 0 };
			heartCommRsp.setMsgContent(basePacket.getSequenceId(), basePacket.getCommandId(), heartBytes);
			sendPacket(ctx.channel(), heartCommRsp);
			
//			ControlPacketRsp controlPacketRsp = new ControlPacketRsp(DeviceCommand.CONTROL_COMMAND_RSP, basePacket.getReserve(), basePacket.getSubcontract(), basePacket.getRsa(), 0, 
//					basePacket.getImei(), getSendSequenceIdHex(), "", "");
//			controlPacketRsp.openBike();
//			SendMessageSocketThread openMessageSocketThread = new SendMessageSocketThread(appId,
//					deviceImei, controlPacketRsp, 10000);
//			NettyChannelMapUtils.sendTaskThreadPool.execute(openMessageSocketThread);
			
			
			AttributePacketRsp attributePacketRsp = new AttributePacketRsp(DeviceCommand.QUERY_ATTRIBUTE_COMMAND_RSP, basePacket.getReserve(), basePacket.getSubcontract(), basePacket.getRsa(), 0, 
					basePacket.getImei(), getSendSequenceIdHex(), "", "");
			attributePacketRsp.queryAttribute();
			SendMessageSocketThread attributeMessageSocketThread = new SendMessageSocketThread(appId,
					deviceImei, attributePacketRsp, 5000);
			NettyChannelMapUtils.sendTaskThreadPool.execute(attributeMessageSocketThread);
			
//			ParamsPacketRsp paramsPacketRsp = new ParamsPacketRsp(DeviceCommand.QUERY_PARAMS_COMMAND_RSP, basePacket.getReserve(), basePacket.getSubcontract(), basePacket.getRsa(), 0, 
//					basePacket.getImei(), getSendSequenceIdHex(), "", "");
//			paramsPacketRsp.queryParams();
//			SendMessageSocketThread paramsMessageSocketThread = new SendMessageSocketThread(appId,
//					deviceImei, paramsPacketRsp, 10000);
//			NettyChannelMapUtils.sendTaskThreadPool.execute(paramsMessageSocketThread);
//			
//			QueryLocationPacketRsp locationPacketRsp = new QueryLocationPacketRsp(DeviceCommand.QUERY_LOCATION_COMMAND_RSP, basePacket.getReserve(), basePacket.getSubcontract(), basePacket.getRsa(), 0, 
//					basePacket.getImei(), getSendSequenceIdHex(), "", "");
//			locationPacketRsp.queryLocation();
//			SendMessageSocketThread locationSocketThread = new SendMessageSocketThread(appId,
//					deviceImei, locationPacketRsp, 15000);
//			NettyChannelMapUtils.sendTaskThreadPool.execute(locationSocketThread);
			break;
		case DeviceCommand.REGISTER_COMMAND_REQ:
			log.info("终端注册   注册码= " + basePacket.getCommandId() + "---" + basePacket.toString());
			RegisterEntity register = new RegisterEntity(basePacket.getBody());
			log.info("终端注册上报 蓝牙地址 = " + register.getBluetooth() + ", iccid = " + register.getIccid());
			deviceInfoService = (DeviceInfoService) ApplicationContextUtil.getBeanById("deviceInfoService");
			deviceInfo = deviceInfoService.getDeviceInfoMapByImei(basePacket.getImei());
		    if(deviceInfo == null) {
		    	throw new Exception(basePacket.getImei() + "设备不存在请联系管理员!!");
		    }
			RegisterPacketRsp registerPacketRsp = new RegisterPacketRsp(DeviceCommand.REGISTER_COMMAND_RSP, basePacket.getReserve(), basePacket.getSubcontract(), basePacket.getRsa(), 0, 
					basePacket.getImei(), basePacket.getSequenceId(), "", "");
			registerPacketRsp.setRegisterResult(getSendSequenceIdHex(), 0, "1234");
//			sendPacket(ctx.channel(), registerPacketRsp);
			break;
		case DeviceCommand.LOCATION_COMMAND_REQ:
			String bodyHex = basePacket.getBody();
			LocationInfoEntity locationInfo = new LocationInfoEntity(bodyHex);
			log.info("终端位置上报 上报码= " + basePacket.getCommandId() + " 锁状态 = " + locationInfo.getLockStatus() + ", UploadType = " + locationInfo.getUploadType() + ", 经度 = " + locationInfo.getLongitude() + ", 纬度 = " + locationInfo.getLatitude());
			if(DictEnum.UploadStatusCode.OPEN_UPLOAD.equals(locationInfo.getUploadType()) || DictEnum.UploadStatusCode.CLOSE_UPLOAD.equals(locationInfo.getUploadType())) {
				log.info("终端锁状态= " + locationInfo.getLockStatus() + "---1 working  0 free");
				NettyChannelMapUtils.callBackSendStatus(appId, deviceImei, 0, DictEnum.CallBackType.STATUS_REPORT, locationInfo.getLockStatus(), callUrl);
			}
			CommPacketRsp locationCommRsp = new CommPacketRsp(DeviceCommand.COMM_COMMAND_RSP, basePacket.getReserve(), basePacket.getSubcontract(), basePacket.getRsa(), basePacket.getBodyLength(), 
					basePacket.getImei(), basePacket.getSequenceId(), basePacket.getBody(), basePacket.getCheck());
			byte[] locationBytes = new byte[] { 0 };
			locationCommRsp.setMsgContent(basePacket.getSequenceId(), basePacket.getCommandId(), locationBytes);
			sendPacket(ctx.channel(), locationCommRsp);
			
			NettyChannelMapUtils.refreshDeviceControlInfoExpire(appId, deviceImei);// 设备有消息上来，在redis里做延期
			break;
		case DeviceCommand.QUERY_LOCATION_COMMAND_REQ:
			String bodyLocationHex = basePacket.getBody().substring(4);
			LocationInfoEntity queryLocationInfo = new LocationInfoEntity(bodyLocationHex);
			log.info("查询终端位置上报 上报码= " + basePacket.getCommandId() + "锁状态 = " + queryLocationInfo.getLockStatus() + ", UploadType = " + queryLocationInfo.getUploadType() + ", 经度 = " + queryLocationInfo.getLongitude() + ", 纬度 = " + queryLocationInfo.getLatitude());
			if(DictEnum.UploadStatusCode.OPEN_UPLOAD.equals(queryLocationInfo.getUploadType()) || DictEnum.UploadStatusCode.CLOSE_UPLOAD.equals(queryLocationInfo.getUploadType())) {
				log.info("终端锁状态= " + queryLocationInfo.getLockStatus() + "---1 working  0 free");
				NettyChannelMapUtils.callBackSendStatus(appId, deviceImei, 0, DictEnum.CallBackType.STATUS_REPORT, queryLocationInfo.getLockStatus(), callUrl);
			}
			break;
		case DeviceCommand.PARAMS_COMMAND_REQ:
			log.info("终端参数返回 返回码= " + basePacket.getCommandId() + "---" + basePacket.toString());
			break;
		case DeviceCommand.ATTRIBUTE_COMMAND_REQ:
			AttributeInfoEntity attributeInfoEntity = new AttributeInfoEntity(basePacket.getBody());
			log.info("终端属性返回 返回码= " + basePacket.getCommandId() + "--蓝牙 mac 地址 = " + attributeInfoEntity.getBluetooth() + "----" + basePacket.toString());
			basePacket.getBody();
			break;
		case DeviceCommand.COMM_COMMAND_REQ:
			log.info("终端通用返回  返回码= " + basePacket.getCommandId() + "---" + basePacket.toString());
			break;
		case DeviceCommand.CONTROL_COMMAND_REQ:
			String openHex = basePacket.getBody().substring(4);
			LocationInfoEntity openLocationInfo = new LocationInfoEntity(openHex);
			log.info("开锁状态 状态码= " + basePacket.getCommandId() + "位置上报 锁状态 = " + openLocationInfo.getLockStatus() + ", UploadType = " + openLocationInfo.getUploadType());
//			if(DictEnum.UploadStatusCode.OPEN_UPLOAD.equals(openLocationInfo.getUploadType()) || DictEnum.UploadStatusCode.CLOSE_UPLOAD.equals(openLocationInfo.getUploadType())) {
//				log.info("终端锁状态= " + openLocationInfo.getLockStatus() + "---1 working  0 free");
//				NettyChannelMapUtils.callBackSendStatus(appId, deviceImei, 0, DictEnum.CallBackType.STATUS_REPORT, openLocationInfo.getLockStatus(), callUrl);
//			}
			CommPacketRsp controlCommRsp = new CommPacketRsp(DeviceCommand.COMM_COMMAND_RSP, basePacket.getReserve(), basePacket.getSubcontract(), basePacket.getRsa(), basePacket.getBodyLength(), 
					basePacket.getImei(), basePacket.getSequenceId(), basePacket.getBody(), basePacket.getCheck());
			byte[] controlBytes = new byte[] { 0 };
			controlCommRsp.setMsgContent(basePacket.getSequenceId(), basePacket.getCommandId(), controlBytes);
			sendPacket(ctx.channel(), controlCommRsp);
			break;
		default:
			break;
		}
	}

	/**
	 * 发送数据包 当没办法提交给队列时使用
	 * 
	 * @param ctx
	 * @param packet
	 */
	public synchronized void sendPacket(Channel channel, BasePacket packet) {
		channel.writeAndFlush(packet).addListener(new ChannelFutureListener() {
			// 数据发送完毕后，触发本事件
			@Override
			public void operationComplete(ChannelFuture future) throws Exception {
				log.debug(logHeader + packet.getCommandId() + "[应答]向设备<" + packet.getCommandId()
						+ ">发送数据成功， 发送内容：" + packet.toString());
			}
		});
	}
	
	/**
	 * 发送消息
	 * 
	 * @param rspJsonMap
	 * @throws Exception
	 */
	public synchronized void sendMessage(BasePacket packet)
			throws Exception {
		if (packet != null && mCtx != null) {
			mCtx.writeAndFlush(packet).addListener(new ChannelFutureListener() {
				// 数据发送完毕后，触发本事件
				@Override
				public void operationComplete(ChannelFuture future) throws Exception {
					log.debug(logHeader + packet.getCommandId() + "[应答]向设备<" + packet.getCommandId()
							+ ">发送数据成功， 发送内容：" + packet.toString());
				}
			});
		}
	}
	
	public synchronized void openBike(String code) throws Exception {
		ControlPacketRsp controlPacketRsp = new ControlPacketRsp(DeviceCommand.CONTROL_COMMAND_RSP, basePacket.getReserve(), basePacket.getSubcontract(), basePacket.getRsa(), 0, 
				basePacket.getImei(), getSendSequenceIdHex(), "", "");
		log.info("openBike" + basePacket.getCommandId() + "向设备<" + basePacket.getCommandId() + ">发送数据成功， 发送code：" + code);
		controlPacketRsp.openBike(code);
		SendMessageSocketThread openMessageSocketThread = new SendMessageSocketThread(appId,
				deviceImei, controlPacketRsp, 0);
		NettyChannelMapUtils.sendTaskThreadPool.execute(openMessageSocketThread);
	}
	
	public void close() throws IOException {
		if (mCtx != null) {
			NettyChannelMapUtils.remove(appId, deviceImei);
			mCtx.close();
		}
	}
	
	private String getSendSequenceIdHex() {
		if(sendSequenceId == 65535) {
			sendSequenceId = 1;
		}
		sendSequenceId++;
		return ByteHelper.shortToHexString(sendSequenceId);
	}
}
